<?php
/**
 * 公共类
 * 处理登录，token验证等问题
 * @author JohnnyDen <413819942@qq.com>
 * @package app\controller
 * @copyright 2018年11月12日
 */
namespace app\controller;

use api\Api;

class Common extends Api {
    
    protected $cryptLevel   = [
        'login'                 => 10,
        'loginByCache'          => 10,
        'loginByDynamicPasswd'  => 10,
        'loginBySecret'         => 10,
        'refreshToken'          => 10,
        'registerFromXPP'       => 10,
    ];
    
    //授权用户验证
    protected $authUserMethods      = [
        //无权访问的action
        'no_access' => [],
        //需要验证的action
        'auth'      => [],
        //不需要在验证的action
        'no_auth'   => ['registerFromXPP', 'idXppUserExists', 'usernameExists', 'loginByDynamicPasswd', 'loginBySecret', 'refreshToken'],
        //以上都未匹配到时，默认：null | true | false
        '_default'  => null,
    ];
    
    /**
     * 开通帐号
     * @todo
     */
    public function registerFromXPP() {
        $user       = in('user');
        // 1、接收参数并校验
        // 2、创建用户数据等
        // 3、创建token并返回
        if ($user['id_xpp_user']) {
            out('empty_id_xpp_user');
        }
        if(!$user['username']){
            out('empty_account');
        }
        if(!$user['nickname']){
            out('empty_nickname');
        }
        if(!$user['secret']){
            out('empty_secret');
        }
        
        if (0 != db('Users')->where('username', $user['username'])->count()) {
            out('account_exists');
        }
        
        $uid    = uuid();
        $salt = create_salt(8);
        $password   = \think\helper\Str::random(6);
        $res    = db('Users')->insert([
            'uid' => $uid,
            'username' => $user['username'],
            'password' => create_md5(md5($password), $salt),
            'view_pwd' => $password,
            'salt' => $salt,
            'reg_time' => time(),
            'reg_ip' => request()->ip(),
            'email' => null,
            'group_id' => 2,
            'mobile' => null,
            'rank' => 1,
            'terminal' => null,
        ]);
        if (!$res) {
            out('exe_fail');
        }
        
        $res = db('UserRXpp')->insert([
            'uid'           => $uid,
            'id_xpp_user'   => $user['id_xpp_user'],
            'secret'        => $user['secret'],
        ]);
        if (!$res) {
            //rollback
            M('Users')->where(array('uid' => $uid))->delete();
            out('exe_fail');
        }
        
        //返回登录信息
        $token  = $this->_createAccessToken(input('get.client_id'), $uid);
        
        out('ok', [
            'uid'           => $uid,
            'token'         => $token,
        ]);
    }
    
    /**
     * 管理员登录（动态密码登录）
     */
    public function loginByDynamicPasswd() {
        $id_client  = input('get.client_id/d');
        $username   = in('username');
        $dynamic_passwd = in('dynamic_passwd');
        $device_key = in('device_key', '');
        
        if (!$username) {
            out('empty_account');
        }
        if (!$dynamic_passwd) {
            out('empty_dynamic_passwd');
        }
        $user   = db('Users')->field('uid,username,nickname,dynamic_passwd,salt,login_id_xpp_user,status')->where('username', $username)->find();
        if (!$user) {
            qLog(json_encode([
                'code'      => 'empty_user',
                'username'  => $username,
                'dynamic_passwd'    => $dynamic_passwd,
            ]), false, RUNTIME_PATH.'logs_login/');
            out('login_error');
        }
        if ($user['status'] == 1) {
            out('user_disabled');
        }
        
        //清空动态密码
        db('Users')->where('uid', $user['uid'])->update([
            'dynamic_passwd'    => '',
            //'login_id_xpp_user' => 0,
        ]);
        
        if ($user['dynamic_passwd'] != create_md5($dynamic_passwd, $user['salt'])) {
            if ($user['login_id_xpp_user']) {
                qLog(json_encode([
                    'code'      => 'error_dynamic_passwd2',
                    'username'  => $username,
                    'dynamic_passwd'    => $dynamic_passwd,
                    'db_user'   => $user,
                ]), false, RUNTIME_PATH.'logs_login/');
            } else {
                qLog(json_encode([
                    'code'      => 'error_dynamic_passwd',
                    'username'  => $username,
                    'dynamic_passwd'    => $dynamic_passwd,
                    'db_user'   => $user,
                ]), false, RUNTIME_PATH.'logs_login/');
            }
            out('login_error');
        }
        unset($user['user_dynamic_passwd'], $user['status'], $user['salt']);
        $user['token']  = $this->_createAccessToken($id_client, $user['uid'], $device_key, $user['login_id_xpp_user']);
        if (!$user['token']) {
            qLog(json_encode([
                'code'      => 'error_token',
                'username'  => $username,
                'dynamic_passwd'    => $dynamic_passwd,
                'db_user'   => $user,
            ]), false, RUNTIME_PATH.'logs_login/');
            out('login_error');
        }
        
        out('ok', $user);
    }
    
    /**
     * 第三方用户登录
     */
    public function loginBySecret() {
        $id_client  = input('get.client_id/d');
        $id_xpp_user= in('id_xpp_user/d');
        $uid        = in('uid');
        $secret     = in('secret');
        
        if (!$id_xpp_user) {
            out('empty_id_xpp_user');
        }
        if (!$uid) {
            out('empty_id_xpp_user');
        }
        if (!$secret) {
            out('empty_secret');
        }
        
        $user_r_xpp = db('UserRXpp')->where(['id_xpp_user' => $id_xpp_user, 'uid' => $uid])->find();
        if (!$user_r_xpp) {
            out('null_user');
        }
        if ($user_r_xpp['secret'] != $secret) {
            out('login_error');
        }
        $user   = db('Users')->field('uid,username,nickname,status')->where('uid', $user_r_xpp['uid'])->find();
        if (!$user) {
            out('login_error', db()->getLastSql());
        }
        if ($user['status'] == 1) {
            out('user_disabled');
        }
        
        unset($user['status']);
        $user['token']  = $this->_createAccessToken($id_client, $user['uid']);
        if (!$user['token']) {
            out('login_error');
        }
        
        out('ok', $user);
    }
    
    /**
     * 刷新token，access_token超时后，调用此方法重新获得token
     */
    public function refreshToken(){
        $refresh_token  = in('refresh_token');
        
        if (!$refresh_token) {
            out('empty_refresh_token');
        }
        
        $token  = db('UserAccessToken')->field('id_client,uid,access_token,expire_time')->where('refresh_token', $refresh_token)->find();
        if (!$token) {
            out('error_refresh_token');
        }
        $time   = time();
        if ($time - $token['expire_time'] > RERESH_TOKEN_TIMEOUT) {
            out('refresh_token_timeout');
        }
        
        // access_token未失效，直接返回
        if ($token['expire_time'] - $time > 60) {
            out('ok', [
                'access_token'  => $token['access_token'],
                'expire_time'   => (int)$token['expire_time'],
                'refresh_token' => $refresh_token,
            ]);
        }
        
        // 验证帐号
        if ($token['uid']) {
            $user = db('Users')->field('status')->where('uid', $token['uid'])->find();
            if (!$user) {
                out('null_user');
            } else if ($user['status'] == 1) {
                out('user_disabled');
            }
        }
        
        // 刷新token
        $md5_key        = rand(100000, 999999).'-'.$token['id_client'].'-'.$token['uid'].'-'.$time;
        $access_token   = md5($md5_key);
        $token          = [
            'access_token'  => $access_token,
            'expire_time'   => $time + ACCESS_TOKEN_TIMEOUT,
            'refresh_token' => md5(rand(100000, 999999).'-'.$md5_key.'-'.$access_token),
        ];
        
        if (!db('UserAccessToken')->where('refresh_token', $refresh_token)->update($token)) {
            out('error_refresh_access_token');
        }
        
        out('ok', $token);
    }
    
    /**
     * 创建access_token
     * @param number $id_client
     * @param number $uid
     * @param string $device_key
     * @param int $login_id_xpp_user
     * @return mixed boolean | array
     */
    protected function _createAccessToken($id_client, $uid, $device_key = null, $login_id_xpp_user = 0){
        $time   = time();
        $map    = [
            'id_client'     => $id_client,
            'uid'       => $uid,
        ];
        if ($device_key) {
            $device_key     = md5($device_key);
            $map['_string'] = "device_key='{$device_key}' or expire_time>".($time + ACCESS_TOKEN_TIMEOUT + 604800);
        }
        db('UserAccessToken')->where($map)->delete();
        
        $md5_key        = rand(100000, 999999).'-'.$id_client.'-'.$uid.'-'.$time;
        $access_token   = md5($md5_key);
        $data           = [
            'uid'           => $uid,
            'access_token'  => $access_token,
            'refresh_token' => md5(rand(100000, 999999).'-'.$md5_key.'-'.$access_token),
            'id_client'     => $id_client,
            'login_time'    => $time,
            'expire_time'   => $time + ACCESS_TOKEN_TIMEOUT,
            'upd_time'      => $time,
            'device_key'    => (string)$device_key,
            'login_id_xpp_user'     => (int)$login_id_xpp_user,
        ];
        
        if (!db('UserAccessToken')->insert($data)) {
            return false;
        }
        
        return [
            'access_token'  => $data['access_token'],
            'expire_time'   => (int)$data['expire_time'],
            'refresh_token' => $data['refresh_token'],
        ];
    }
    
    /**
     * 检测id_xpp_user是否已登记
     */
    public function idXppUserExists() {
        $id_xpp_user    = in('id_xpp_user/d');
        $is_exists      = 0;
        if (0 != db('UserRXpp')->where('id_xpp_user', $id_xpp_user)->count()) {
            $is_exists  = 1;
        }
        
        out('ok', [
            'is_exists' => $is_exists,
        ]);
    }
    
    /**
     * 检测帐号是否已登记
     */
    public function usernameExists() {
        $username       = in('username');
        $is_exists      = 0;
        if (0 != db('Users')->where('username', $username)->count()) {
            $is_exists  = 1;
        }
        
        out('ok', [
            'is_exists' => $is_exists,
        ]);
    }
    
}